package com.ficus.road.maintain.ditalTwin.service;

import com.ficus.road.maintain.config.DiseaseConfig;
import com.ficus.road.maintain.core.model.Hit;
import com.ficus.road.maintain.core.model.digitalTwin.*;
import com.ficus.road.maintain.core.model.oss.AliyunOssResult;
import com.ficus.road.maintain.ditalTwin.dao.AssetDao;
import com.ficus.road.maintain.ditalTwin.dao.DigitalHitDao;
import com.ficus.road.maintain.ditalTwin.dao.FrameDao;
import com.ficus.road.maintain.grpc.FrameMetaGrpcServer;
import com.ficus.road.maintain.util.AliyunOSSUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.protobuf.ByteString;
import ficus.car_demo.FrameMeta;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.GZIPOutputStream;

@Service
@Slf4j
public class ImportService {
    @Autowired
    FrameDao frameDao;
    @Autowired
    AliyunOSSUtil aliyunOSSUtil;
    @Autowired
    AssetDao assetDao;

    @Autowired
    DigitalRoadService digitalRoadService;
    @Autowired
    DiseaseConfig diseaseConfig;
    @Autowired
    DigitalHitService digitalHitService;
    String roadName = "锦绣东路";
    String roadId = "6422d128cafca33dc4cac859";
    Set<String> trackIdMap = Sets.newConcurrentHashSet();
    long time = 0;

    @SneakyThrows
    public void importFile(File file) {

        FrameMeta.FrameResultList frameResultList = FrameMeta.FrameResultList.parseFrom(new FileInputStream(file));
        importData(frameResultList);
    }

    @SneakyThrows
    public void importFile(byte[] bytes) {
        FrameMeta.FrameResultList frameResultList = FrameMeta.FrameResultList.parseFrom(bytes);
        importData(frameResultList);
    }

    public void importData(FrameMeta.FrameResultList frameResultList) {


        for (FrameMeta.FrameResult frameResult : frameResultList.getResultListList()) {
            long timestamp = frameResult.getTimeStamp() / 1000;
            //生成历史告警
            Frame frame = new Frame();
            frame.setCreationTime(new Date());
            frame.setTimestamp(timestamp);
            GpsResult gpsResult = GpsResult.builder()
                    .latitude(frameResult.getGpsResult().getLatitude())
                    .longitude(frameResult.getGpsResult().getLongitude())
                    .build();
            frame.setLocation(gpsResult);
            System.out.println(frameResult.getTimeStamp());
            List<Asset> assets = Lists.newArrayList();
            final AliyunOssResult[] framePicResult = {null};
            final AliyunOssResult[] assetsPicResult = {null};
            List<Hit> hitList = null;
            frameResult.getPerceptionResultSet().getRoadPerceptionResultListList().forEach(roadPerceptionResult -> {

                FrameMeta.RoadPerceptionResult.ResultCase resultCase = roadPerceptionResult.getResultCase();
                Asset asset = new Asset();
                asset.setCode(resultCase.getNumber());
                asset.setTimestamp(timestamp);
                asset.setCreationTime(new Date());
                asset.setGpsResult(gpsResult);
                asset.setLocation(Lists.newArrayList(gpsResult.getLongitude(), gpsResult.getLatitude()));
                asset.setType(resultCase.toString());

                Object result_ = getFieldValueByFieldName("result_", roadPerceptionResult);
                String trackId = resultCase + "_" + getFieldValueByFieldName("trackId_", result_);
                if (!trackIdMap.contains(trackId)) {
                    trackIdMap.add(trackId);
                    FrameMeta.Bbox bbox = (FrameMeta.Bbox) getFieldValueByFieldName("bbox_", result_);
                    asset.setBBox(BBox.builder().x(bbox.getX()).y(bbox.getY()).w(bbox.getW()).h(bbox.getH()).build());

                    FrameMeta.RelBbox relBbox = (FrameMeta.RelBbox) getFieldValueByFieldName("relBbox_", result_);
                    asset.setRelBBox(RelBBox.builder().x(relBbox.getX()).y(relBbox.getY()).w(relBbox.getW()).h(relBbox.getH()).build());

                    FrameMeta.Point point = (FrameMeta.Point) getFieldValueByFieldName("point_", result_);
                    asset.setPoint(Point.builder().x(point.getX()).y(point.getY()).build());

                    FrameMeta.RelPoint relPoint = (FrameMeta.RelPoint) getFieldValueByFieldName("relPoint_", result_);
                    asset.setRelPoint(RelPoint.builder().x(relPoint.getX()).y(relPoint.getY()).build());

                    FrameMeta.Cube cube = (FrameMeta.Cube) getFieldValueByFieldName("cube_", result_);
                    asset.setCube(Cube.builder()
                            .p1(Point.builder().x(cube.getP1().getX()).y(cube.getP1().getY()).build())
                            .p2(Point.builder().x(cube.getP2().getX()).y(cube.getP2().getY()).build())
                            .p3(Point.builder().x(cube.getP3().getX()).y(cube.getP3().getY()).build())
                            .p4(Point.builder().x(cube.getP4().getX()).y(cube.getP4().getY()).build())
                            .p5(Point.builder().x(cube.getP5().getX()).y(cube.getP5().getY()).build())
                            .p6(Point.builder().x(cube.getP6().getX()).y(cube.getP6().getY()).build())
                            .p7(Point.builder().x(cube.getP7().getX()).y(cube.getP7().getY()).build())
                            .p8(Point.builder().x(cube.getP8().getX()).y(cube.getP8().getY()).build())
                            .build());
                    FrameMeta.RelCube relCube = (FrameMeta.RelCube) getFieldValueByFieldName("relCube_", result_);
                    asset.setRelCube(RelCube.builder()
                            .p1(RelPoint.builder().x(relCube.getRelP1().getX()).y(relCube.getRelP1().getY()).build())
                            .p2(RelPoint.builder().x(relCube.getRelP2().getX()).y(relCube.getRelP2().getY()).build())
                            .p3(RelPoint.builder().x(relCube.getRelP3().getX()).y(relCube.getRelP3().getY()).build())
                            .p4(RelPoint.builder().x(relCube.getRelP4().getX()).y(relCube.getRelP4().getY()).build())
                            .p5(RelPoint.builder().x(relCube.getRelP5().getX()).y(relCube.getRelP5().getY()).build())
                            .p6(RelPoint.builder().x(relCube.getRelP6().getX()).y(relCube.getRelP6().getY()).build())
                            .p7(RelPoint.builder().x(relCube.getRelP7().getX()).y(relCube.getRelP7().getY()).build())
                            .p8(RelPoint.builder().x(relCube.getRelP8().getX()).y(relCube.getRelP8().getY()).build())
                            .build());
                    asset.setCreationTime(new Date());
                    asset.setTrackId(trackId);
                    asset.setCreationTime(new Date());
                    if (null == framePicResult[0]) {
                        framePicResult[0] = aliyunOSSUtil.uploadImg("digitalTwin/" + UUID.randomUUID().toString().replace("-", "") + ".jpg",
                                frameResult.getPicture().toByteArray());
                        asset.setPicUrl(framePicResult[0].getUrl());
                    }
                    if (null == assetsPicResult[0]) {
                        assetsPicResult[0] = aliyunOSSUtil.uploadImg("digitalTwin/" + UUID.randomUUID().toString().replace("-", "") + ".jpg",
                                frameResult.getPerceptionResultSet().getSegmentPictureMask().toByteArray());
                        asset.setAssetMaskPicUrl(assetsPicResult[0].getUrl());
                    }

                    asset.setRoadId(roadId);
                    if (digitalRoadService.getElementTypes().contains(asset.getType())) {
                        assetDao.insert(asset);
                    }

                    assets.add(asset);
                } else {
                    System.out.println("trackId 重复" + trackId);
                }
            });

//            frameResult.getDiseaseResultListList().forEach(diseaseResult -> {
//                DigitalTwin.RoadDiseaseResult.ResultCase resultCase = diseaseResult.getResultCase();
//                HitSubject hitSubject = new HitSubject();
//                hitSubject.setDiseaseUid(diseaseResult.getDiseaseUid());
//                String type = resultCase.toString();
//                hitSubject.setType(resultCase.toString());
//                Object result_ = getFieldValueByFieldName("result_", diseaseResult);
//
//                Object o = getFieldValueByFieldName("diseaseType_", result_);
//                System.out.println(o.toString());
//                DigitalTwin.PicCrackType crackType = null;//t.getPicCrackResult().getCrackType();
//                String subType = crackType.toString();
//                String finalType = StringUtils.isEmpty(subType) ? type : type + "_" + subType;
//                DiseaseConfigInfo diseaseConfigInfo = diseaseConfig.getMap().get(finalType);
//
//                DigitalTwin.Bbox bbox = (DigitalTwin.Bbox)getFieldValueByFieldName("result_", diseaseResult);
//                hitSubject.setBbox(Bbox.builder().x(bbox.getX()).y(bbox.getY()).w(bbox.getW()).h(bbox.getH()).build());
//
//                DigitalTwin.RelBbox relBbox = (DigitalTwin.RelBbox) getFieldValueByFieldName("relBbox_", result_);
//                hitSubject.setRelBbox(RelBbox.builder().x(relBbox.getX()).y(relBbox.getY()).w(relBbox.getW()).h(relBbox.getH()).build());
//                Hit hit = new Hit();
//                HitAttrs attrs = new HitAttrs();
//                attrs.setRoadName(roadName);
//                attrs.setStatus(HitStatusEnum.NEW);
//                attrs.setSceneImage(framePicResult.getUrl());
//                attrs.setFrameImageUrl(framePicResult.getUrl());
//                attrs.setLoop(3);
//                attrs.setDiseaseUid(diseaseResult.getDiseaseUid());
//                hit.setAttrs(attrs);
//                hit.setHitTime(timestamp);
//                hit.setFirstHitTime(timestamp);
//                hit.setSubjects(Lists.newArrayList(hitSubject));
//                hit.setType(resultCase.toString());
//                hits.add(hit);
//            });

            if (!CollectionUtils.isEmpty(frameResult.getDiseaseResultListList())) {
                if (null == framePicResult[0]) {
                    framePicResult[0] = aliyunOSSUtil.uploadImg("digitalTwin/" + UUID.randomUUID().toString().replace("-", "") + ".jpg",
                            frameResult.getPicture().toByteArray());
                    frame.setPicUrl(framePicResult[0].getUrl());
                }
//                List<Hit> hits = digitalHitService.process(frameResult, roadName, framePicResult[0].getUrl());
//                if (!CollectionUtils.isEmpty(hits)){
//                    hitList = hits;
//                }
            }

            if (timestamp - time < 1000) {
                continue;
            }

            if (null == framePicResult[0]) {
                framePicResult[0] = aliyunOSSUtil.uploadImg("digitalTwin/" + UUID.randomUUID().toString().replace("-", "") + ".jpg",
                        frameResult.getPicture().toByteArray());
                frame.setPicUrl(framePicResult[0].getUrl());
            }
            if (null == assetsPicResult[0]) {
                assetsPicResult[0] = aliyunOSSUtil.uploadImg("digitalTwin/" + UUID.randomUUID().toString().replace("-", "") + ".jpg",
                        frameResult.getPerceptionResultSet().getSegmentPictureMask().toByteArray());

                frame.setAssetMaskPicUrl(assetsPicResult[0].getUrl());
            }
            time = timestamp;
            frame.setAssets(assets);
            if (hitList != null) {
                frame.setHits(hitList);
            }
            System.out.println(frame);
            frameDao.insert(frame);
        }
    }

    /**
     * 根据属性名获取属性值
     */
    private Object getFieldValueByFieldName(String fieldName, Object object) {
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            //设置对象的访问权限，保证对private的属性的访问
            field.setAccessible(true);
            return field.get(object);
        } catch (Exception e) {
            return null;
        }
    }

    @Autowired
    DigitalHitDao hitDao;

    //    @PostConstruct
    public void initFrameHit() {
        Map<Long, Frame> frameMap = frameDao.findAll().stream().collect(Collectors.toMap(Frame::getTimestamp, v -> v));
        Map<Long, List<Hit>> hitMap = hitDao.findAll().stream().collect(Collectors.groupingBy(t -> t.getHitTime() / 1000));
        hitMap.forEach((time, list) -> {
            Frame frame = frameMap.get(time);
            if (frame != null) {
                frame.setHits(list);
                frameDao.saveOrUpdate(frame);
            }
        });
        System.out.println(1);
    }

    private String carId = "JINXIUDONGLU";
    public static DateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    public static final String PIC = "pic";
    public static final String DISEASE = "disease";
    public static final String INSPECT = "inspect";
    public static final String prefix = "digitalTwin";
    private Long lastTimestamp = 0L;
    public static final int INTERVAL = 4800;
    private static volatile Long lastInspectTime = 0L;


    @Autowired
    FrameMetaGrpcServer frameMetaGrpcServer;
    List<Asset> assets = Lists.newArrayList();

    public void importMetaV2(List<FrameMeta.FrameResult> list) {
        long timeStamp = list.get(0).getTimeStamp() / 1000;
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        String formatDate = sdf.format(new Timestamp(timeStamp));
        String directory = generateDirectory(carId, formatDate);
        String inspectDirectory = directory.replace(PIC, INSPECT);
        String fileName = directory + "/" + timeStamp;

        FrameMeta.PicArray.Builder picArrayBuilder = FrameMeta.PicArray.newBuilder();
        FrameMeta.PicArray.Builder diseasePicArrayBuilder = FrameMeta.PicArray.newBuilder();
        List<FrameMeta.FrameResult> newList = new ArrayList<>();
        int i = 0;
        int j = 0;

        List<FrameMeta.InspectPic> inspectPics = new ArrayList<>();

        for (FrameMeta.FrameResult frame : list) {

            GpsResult gpsResult = GpsResult.builder()
                    .latitude(frame.getGpsResult().getLatitude())
                    .longitude(frame.getGpsResult().getLongitude())
                    .build();

            FrameMeta.FrameResult.Builder builder = frame.toBuilder();
            builder.setCarId(carId);
            long timestamp = frame.getTimeStamp() / 1000;
            builder.setTimeStamp(timestamp);
            if (!frame.getPicture().isEmpty()) {
                picArrayBuilder.addPics(frame.getPicture());
                builder.setPicture(ByteString.copyFromUtf8("" + i++));
            }
            if (!frame.getLidarResult().getPicture().isEmpty()) {
                picArrayBuilder.addPics(frame.getLidarResult().getPicture());
                builder.setLidarResult(builder.getLidarResultBuilder().setPicture(ByteString.copyFromUtf8("" + i++)).build());
            }
            if (!frame.getPerceptionResultSet().getSegmentPicture().isEmpty()) {
                picArrayBuilder.addPics(frame.getPerceptionResultSet().getSegmentPicture());
                builder.setPerceptionResultSet(builder.getPerceptionResultSetBuilder().setSegmentPicture(ByteString.copyFromUtf8("" + i++)).build());
            }
//            else if (!frame.getPerceptionResultSet().getSegmentPictureMask().isEmpty()) {
//                picArrayBuilder.addPics(frame.getPerceptionResultSet().getSegmentPictureMask());
//                builder.setPerceptionResultSet(builder.getPerceptionResultSetBuilder().setSegmentPicture(ByteString.copyFromUtf8("" + i++)).build());
//            }
            final AliyunOssResult[] framePicResult = {null};
            final AliyunOssResult[] assetsPicResult = {null};
            byte[] byteArray = frame.getPicture().toByteArray();
            byte[] byteArray1 = frame.getPerceptionResultSet().getSegmentPictureMask().toByteArray();
            frame.getPerceptionResultSet().getRoadPerceptionResultListList().forEach(roadPerceptionResult -> {

                FrameMeta.RoadPerceptionResult.ResultCase resultCase = roadPerceptionResult.getResultCase();
                Asset asset = new Asset();
                asset.setCode(resultCase.getNumber());
                asset.setTimestamp(timestamp);
                asset.setCreationTime(new Date());
                asset.setGpsResult(gpsResult);
                asset.setLocation(Lists.newArrayList(gpsResult.getLongitude(), gpsResult.getLatitude()));
                asset.setType(resultCase.toString());

                Object result_ = getFieldValueByFieldName("result_", roadPerceptionResult);
                String trackId = resultCase + "_" + getFieldValueByFieldName("trackId_", result_);
                if (!trackIdMap.contains(trackId)) {
                    trackIdMap.add(trackId);
                    FrameMeta.Bbox bbox = (FrameMeta.Bbox) getFieldValueByFieldName("bbox_", result_);
                    asset.setBBox(BBox.builder().x(bbox.getX()).y(bbox.getY()).w(bbox.getW()).h(bbox.getH()).build());

                    FrameMeta.RelBbox relBbox = (FrameMeta.RelBbox) getFieldValueByFieldName("relBbox_", result_);
                    asset.setRelBBox(RelBBox.builder().x(relBbox.getX()).y(relBbox.getY()).w(relBbox.getW()).h(relBbox.getH()).build());

                    FrameMeta.Point point = (FrameMeta.Point) getFieldValueByFieldName("point_", result_);
                    asset.setPoint(Point.builder().x(point.getX()).y(point.getY()).build());

                    FrameMeta.RelPoint relPoint = (FrameMeta.RelPoint) getFieldValueByFieldName("relPoint_", result_);
                    asset.setRelPoint(RelPoint.builder().x(relPoint.getX()).y(relPoint.getY()).build());

                    FrameMeta.Cube cube = (FrameMeta.Cube) getFieldValueByFieldName("cube_", result_);
                    asset.setCube(Cube.builder()
                            .p1(Point.builder().x(cube.getP1().getX()).y(cube.getP1().getY()).build())
                            .p2(Point.builder().x(cube.getP2().getX()).y(cube.getP2().getY()).build())
                            .p3(Point.builder().x(cube.getP3().getX()).y(cube.getP3().getY()).build())
                            .p4(Point.builder().x(cube.getP4().getX()).y(cube.getP4().getY()).build())
                            .p5(Point.builder().x(cube.getP5().getX()).y(cube.getP5().getY()).build())
                            .p6(Point.builder().x(cube.getP6().getX()).y(cube.getP6().getY()).build())
                            .p7(Point.builder().x(cube.getP7().getX()).y(cube.getP7().getY()).build())
                            .p8(Point.builder().x(cube.getP8().getX()).y(cube.getP8().getY()).build())
                            .build());
                    FrameMeta.RelCube relCube = (FrameMeta.RelCube) getFieldValueByFieldName("relCube_", result_);
                    asset.setRelCube(RelCube.builder()
                            .p1(RelPoint.builder().x(relCube.getRelP1().getX()).y(relCube.getRelP1().getY()).build())
                            .p2(RelPoint.builder().x(relCube.getRelP2().getX()).y(relCube.getRelP2().getY()).build())
                            .p3(RelPoint.builder().x(relCube.getRelP3().getX()).y(relCube.getRelP3().getY()).build())
                            .p4(RelPoint.builder().x(relCube.getRelP4().getX()).y(relCube.getRelP4().getY()).build())
                            .p5(RelPoint.builder().x(relCube.getRelP5().getX()).y(relCube.getRelP5().getY()).build())
                            .p6(RelPoint.builder().x(relCube.getRelP6().getX()).y(relCube.getRelP6().getY()).build())
                            .p7(RelPoint.builder().x(relCube.getRelP7().getX()).y(relCube.getRelP7().getY()).build())
                            .p8(RelPoint.builder().x(relCube.getRelP8().getX()).y(relCube.getRelP8().getY()).build())
                            .build());
                    asset.setCreationTime(new Date());
                    asset.setTrackId(trackId);
                    asset.setCreationTime(new Date());

                    if (null == framePicResult[0]) {
                        framePicResult[0] = aliyunOSSUtil.uploadImg("digitalTwin/" + carId + "/pic/" + UUID.randomUUID().toString().replace("-", "") + ".jpg",
                                byteArray);
                        asset.setPicUrl(framePicResult[0].getUrl());
                    }
                    if (null == assetsPicResult[0]) {
                        assetsPicResult[0] = aliyunOSSUtil.uploadImg("digitalTwin/" + carId + "/segment/" + UUID.randomUUID().toString().replace("-", "") + ".jpg",
                                byteArray1);
                        asset.setAssetMaskPicUrl(assetsPicResult[0].getUrl());
                    }

                    asset.setRoadId(roadId);
                    if (digitalRoadService.getElementTypes().contains(asset.getType())) {
                        assetDao.insert(asset);
                    }

                    assets.add(asset);
                } else {
                    System.out.println("trackId 重复" + trackId);
                }
            });
            if (!frame.getPictureForDisease().isEmpty()) {
                diseasePicArrayBuilder.addPics(frame.getPictureForDisease());
                builder.setPictureForDisease(ByteString.copyFromUtf8("" + j++));
                if (frame.getTimeStamp() / 1000 - lastInspectTime > INTERVAL) {
                    String inspectFileName = inspectDirectory + "/" + frame.getTimeStamp() / 1000;
                    String url = aliyunOSSUtil.uploadImg(inspectFileName, frame.getPictureForDisease().toByteArray()).getUrl();
                    FrameMeta.InspectPic inspectPic = FrameMeta.InspectPic.newBuilder().setTimestamp(frame.getTimeStamp() / 1000).setUrl(url).build();
                    inspectPics.add(inspectPic);
                    lastInspectTime = frame.getTimeStamp() / 1000;
                }
            }
            newList.add(builder.build());
            frame = builder.build();

            if (lastTimestamp != 0 && frame.getTimeStamp() / 1000 - lastTimestamp < 2) {
                log.debug("send gps break, time is not enough, time = {}, lastTime = {}", timeStamp, lastTimestamp);
                // 直接把这段gps数据给丢了
                continue;
            }
            FrameMeta.GpsRequest gpsRequest = FrameMeta.GpsRequest.newBuilder()
                    .setCarId(carId)
                    .setGpsResult(frame.getGpsResult())
                    .setTimestamp(frame.getTimeStamp())
                    .build();
            FrameMeta.GpsListRequest gpsListRequest = FrameMeta.GpsListRequest.newBuilder().addAllGpsRequest(Lists.newArrayList(gpsRequest)).build();

            frameMetaGrpcServer.sendGpsRequest1(gpsListRequest);
            lastTimestamp = frame.getTimeStamp() / 1000;
        }
//        log.info("start save pic to oss, sequenceId:{}, size:{}", list.get(0).getSequenceId(), picArrayBuilder.build().toByteArray().length);
        byte[] compress = compress(picArrayBuilder.build().toByteArray());
        aliyunOSSUtil.uploadData(fileName, compress);
//        log.info("start save disease to oss, sequenceId:{}, size:{}", list.get(0).getSequenceId(), diseasePicArrayBuilder.build().toByteArray().length);
        String diseaseFileName = fileName.replace(PIC, DISEASE);
        aliyunOSSUtil.uploadData(diseaseFileName, diseasePicArrayBuilder.build().toByteArray());
        FrameMeta.SendMetaRequest request = FrameMeta.SendMetaRequest.newBuilder().addAllFrames(newList)
                .setCarId(carId).setEndTime(list.get(list.size() - 1).getTimeStamp() / 1000).setPicturePath(fileName)
                .setStartTime(list.get(0).getTimeStamp() / 1000).addAllInspectPics(inspectPics).setIsCompressed(true).build();
        frameMetaGrpcServer.sendMetaInfo1(request);
    }

    private String generateDirectory(String carId, String formatDate) {
        return prefix + "/" + carId + "/" + formatDate + "/" + PIC;
    }

    public static byte[] compress(byte[] bytes) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(bytes);
            gzip.close();
        } catch (IOException e) {
            log.error("gzip compress error.", e);
        }
        return out.toByteArray();
    }
}
